<body></body>
<script>
  let activeEffect = null
  const effectStack = [] //effect栈
  const bucket = new WeakMap()

  const data = {
    foo: 1,
    bar: 2
  }

  const obj = new Proxy(data, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, newVal) {
      target[key] = newVal
      trigger(target, key)
    }
  })

  function track(target, key) {
    if (!activeEffect) return
    let depMap = bucket.get(target)
    if (!depMap) {
      bucket.set(target, (depMap = new Map()))
    }

    let deps = depMap.get(key)
    if (!deps) {
      depMap.set(key, (deps = new Set()))
    }
    const effectSet = deps.add(activeEffect)
    activeEffect.deps.push(effectSet)
  }

  function trigger(target, key) {
    let depMap = bucket.get(target)
    if (!depMap) return
    const depsSet = depMap.get(key)
    const effectsToRun = new Set(depsSet)
    effectsToRun &&
      effectsToRun.forEach(effectFn => {
        if (effectFn?.options?.scheduler) {
          effectFn.options.scheduler(effectFn) //执行调度
        } else {
          effectFn()
        }
      })
  }

  function effect(fn, options) {
    // 这里的 effectFn是一个独立函数，就是每一次effect开辟一个新的空间，使用的是新的指针
    function effectFn() {
      cleanup(effectFn)
      activeEffect = effectFn

      effectStack.push(effectFn)
      const res = fn()
      effectStack.pop(effectFn)
      activeEffect = effectStack[effectStack.length - 1]
      return res
    }
    effectFn.options = options
    effectFn.deps = [] //反向收集 effectSet的引用
    if (!effectFn.options.lazy) effectFn()
    return effectFn
  }
  function cleanup(effectFn) {
    effectFn.deps.forEach(depSet => {
      depSet.delete(effectFn) //遍历所有的 Set集合删除同一个副作用
    })
    effectFn.deps.length = 0
  }
  // 任务队列控制逻辑
  const jobQueue = new Set() // 任务队列（自动去重）
  let isFlushing = false // 队列刷新状态
  const p = Promise.resolve() // 微任务触发器

  function flushJob() {
    if (isFlushing) return
    isFlushing = true
    p.then(() => {
      jobQueue.forEach(job => job())
    }).finally(() => {
      isFlushing = false
      jobQueue.clear()
    })
  }

  //   1、实现一个计算属性
  function computed(getter) {
    //把 getter 作为副作用函数，创建一个 lazy 的 effect
    const effectFn = effect(getter, {
      lazy: true
    })
    //当读取value时才执行effectFn
    const obj = {
      get value() {
        return effectFn()
      }
    }
    return obj
  }
  const sumRes = computed(() => obj.foo + obj.bar)
  console.log(sumRes.value)
  console.log(sumRes.value)
  console.log(sumRes.value)
  // 可以看到它能够正确地工作。不过现在我们实现的计算属性只做到了懒计算，
  // 也就是说，只有当你真正读取 sumRes.value 的值时，它才会进行计算并得到值。
  // 但是还做不到对值进行缓存即假如我们多次访问 sumRes.value 的值,
  // 会导致effectFn进行多次计算,即使obj.foo 和 obj.bar的值本身并没有变化:
  // console.log(sumRes.value)//3
  // console.log(sumRes,value)//3
  // console.log(sumRes.value)//3
</script>
